# -*- coding: utf-8 -*-

import struct
import re
import os
import math
from tkinter import *
from tkinter import ttk

def clk_fre_to_cycle(clk_fre = "", hz_mode = 1): #频率转周期
	if clk_fre == "":
		return "", ""
	
	clk_fre = float(clk_fre)
	#单位统一到Hz，即1/秒
	if hz_mode == 0: #KHz
		clk_fre = clk_fre * 1000
	elif hz_mode == 1: #Mhz
		clk_fre = clk_fre * 1000000
	else: #GHz
		clk_fre = clk_fre * 1000000000
	#计算时钟周期，单位以ps为准
	clk_cycle = float(1/clk_fre) * 1000000000000
	#返回频率单位为Hz，周期单位为ps，均为字符串
	return str(clk_fre), str(clk_cycle)
pass

def clk_cycle_to_fre(clk_cycle = "", ns_mode = 2): #周期转频率
	if clk_cycle == "":
		return "", ""
	
	clk_cycle = float(clk_cycle)
	#计算时钟周期，单位以ps为准
	if ns_mode == 0: #ms
		clk_cycle = clk_cycle * 1000000000
	elif ns_mode == 1: #μs
		clk_cycle = clk_cycle * 1000000
	elif ns_mode == 2: #ns
		clk_cycle = clk_cycle * 1000
	else: #ps
		clk_cycle = clk_cycle
	#单位统一到Hz，即1/秒
	clk_fre = float(1/clk_cycle) * 1000000000000
	#返回频率单位为Hz，周期单位为ps，均为字符串
	return str(clk_fre), str(clk_cycle)
pass

def sig_wd_to_bit(sig_wd = "", wd_mode = 0):
	if sig_wd == "":
		return sig_wd
	
	sig_wd = int(sig_wd)
	if wd_mode == 0:      #bit -> bit
		sig_wd = sig_wd
	else:
		sig_wd = sig_wd*8 #byte -> bit
	return str(sig_wd)
pass

def bd_perf_to_bps(bd_perf = "", bd_mode = 0):
	if bd_perf == "":
		return bd_perf
	
	bd_perf = float(bd_perf)
	if bd_mode == 0: #Kbps
		bd_perf = bd_perf * 1000
	elif bd_mode == 1: #Mbps
		bd_perf = bd_perf * 1000000
	elif bd_mode == 2: #Gbps
		bd_perf = bd_perf * 1000000000
	else: #Tbps
		bd_perf = bd_perf * 1000000000000
	return str(bd_perf)
pass

def gen_print_clk_fre(clk_fre = "", hz_mode = 2): #需要打印出来的频率，输入必须是Hz为单位
	if clk_fre == "":
		return clk_fre
	
	clk_fre = float(clk_fre)
	if hz_mode == 0: #KHz
		clk_fre = clk_fre / 1000
	elif hz_mode == 1: #Mhz
		clk_fre = clk_fre / 1000000
	else: #GHz
		clk_fre = clk_fre / 1000000000
	return str(clk_fre)
pass

def gen_print_clk_cycle(clk_cycle = "", hz_mode = 2):
	if clk_cycle == "":
		return clk_cycle
	
	clk_cycle = float(clk_cycle)
	if ns_mode == 0: #ms
		clk_cycle = clk_cycle / 1000000000
	elif ns_mode == 1: #μs
		clk_cycle = clk_cycle / 1000000
	elif ns_mode == 2: #ns
		clk_cycle = clk_cycle / 1000
	else: #ps
		clk_cycle = clk_cycle
	return str(clk_cycle)
pass

def bound_width_to_clk(bd_perf = "", sig_wd = 0):
	#sig_wd/clk_cyc = bd_perf
	bd_perf = float(bd_perf)
	sig_wd  = float(sig_wd)
	clk_cycle = sig_wd / bd_perf #bit/(bit/s) = s
	print(bd_perf, "bps", sig_wd, "bit", clk_cycle, "s")
	clk_cycle = clk_cycle * 1000000000000 #to ps
	return str(clk_cycle)
pass

def bound_clk_to_width(bd_perf = "", clk_cycle = ""):
	if bd_perf =="" or clk_cycle == "":
		return ""
	
	bd_perf   = float(bd_perf)   #bps
	clk_cycle = float(clk_cycle) #ps
	sig_wd    = (clk_cycle/1000000000000) * bd_perf #bit
	sig_wd    = math.ceil(sig_wd)
	return str(sig_wd)
pass

def clk_width_to_bound(clk_cycle = "", sig_wd = ""): #ps, bit
	if clk_cycle == "" or sig_wd == "":
		return ""
	
	clk_cycle = float(clk_cycle)
	sig_wd    = float(sig_wd)
	bd_perf   = sig_wd/(clk_cycle/1000000000000) #bit/s
	return str(bd_perf)
pass


def gen_print_sig_wd(sig_wd = "", wd_mode = ""): #sig_wd单位是bit，wd_mode是目标单位
	if sig_wd == "" or wd_mode == "":
		return ""
	
	sig_wd = int(sig_wd)
	if wd_mode == 0:      #bit -> bit
		sig_wd = sig_wd
	else:
		sig_wd = math.ceil(sig_wd/8) #bit -> byte
	return str(sig_wd)
pass

def gen_print_bd_perf(bd_perf = "", bd_mode = 0): #bd_perf单位是bps，bd_mode是目标单位
	if bd_perf == "":
		return ""
	
	bd_perf = float(bd_perf)
	if bd_mode == 0: #Kbps
		bd_perf = bd_perf/1000 #bps -> kbps
	elif bd_mode == 1: #Mbps
		bd_perf = bd_perf/1000000 #bps -> Mbps
	elif bd_mode == 2: #Gbps
		bd_perf = bd_perf/1000000000 #bps -> Gbps
	else:
		bd_perf = bd_perf/1000000000000 #bps -> Tbps
	return str(bd_perf)
pass

def tk_main():
	#参数
	txt_wd = 13
	com_wd = 5
	#全局变量
	global sel_input_v
	global hz_mode
	global ns_mode
	global wd_mode
	global bd_mode

	#选择框取值需要有默认值
	hz_mode = 1
	ns_mode = 2
	wd_mode = 0
	bd_mode = 2

	def hz_mode_choose(event): #频率选择框
		global hz_mode
		hz_mode = hz_com.current()
	def ns_mode_choose(event): #周期选择框
		global ns_mode
		ns_mode = ns_com.current()
	def wd_mode_choose(event): #周期选择框
		global wd_mode
		wd_mode = sig_com.current()
	def bd_mode_choose(event): #周期选择框
		global bd_mode
		bd_mode = bd_com.current()

	def start_trans():
		p_fre   = 0 #待打印的时钟频率
		p_cycle = 0 #待打印的时钟周期

		model = sel_input_v.get() #1以带宽和位宽算时钟 #2以时钟和位宽算带宽
		clk_fre   = txt_hz.get(1.0, "end").strip()  #时钟频率
		clk_cycle = txt_ns.get(1.0, "end").strip()  #时钟周期
		sig_wd    = txt_sig.get(1.0, "end").strip() #信号位宽
		bd_perf   = txt_bd.get(1.0, "end").strip()  #通路带宽
		
		#分一下类
		trans_mode = ""
		if model == 1: #有通路带宽
			if clk_fre == "" and clk_cycle == "": #没有时钟
				trans_mode = "bound_width_to_clk" #由带宽和位宽推所需时钟
			else:
				trans_mode = "bound_clk_to_width" #由带宽和时钟推所需位宽
		else: #有时钟
			if sig_wd != "": 
				trans_mode = "clk_width_to_bound" #由时钟和位宽推最大通路带宽
			else:
				trans_mode = "clk_bound_to_width" #由时钟和带宽推可以支撑位宽
		
		print(trans_mode)
		
		#换算公式：位宽/时钟周期 = 带宽
		#位宽为bit，周期为s，带宽为bps
		if trans_mode == "bound_width_to_clk": #由带宽和位宽推所需时钟
			sig_wd  = sig_wd_to_bit(sig_wd, wd_mode)
			bd_perf = bd_perf_to_bps(bd_perf, bd_mode)
			clk_cycle = bound_width_to_clk(bd_perf, sig_wd) #单位ps, ns_mode = 3
			clk_fre, clk_cycle = clk_cycle_to_fre(clk_cycle, 3)
		elif trans_mode == "bound_clk_to_width": #由带宽和时钟推所需位宽
			if clk_fre != "": #有时钟频率
				clk_fre, clk_cycle = clk_fre_to_cycle(clk_fre, hz_mode)
			else: #有时钟周期
				clk_fre, clk_cycle = clk_fre_to_cycle(clk_fre, hz_mode)
			bd_perf = bd_perf_to_bps(bd_perf, bd_mode)
			sig_wd  = bound_clk_to_width(bd_perf, clk_cycle)
		elif trans_mode == "clk_width_to_bound": #由频率和位宽推最大通路带宽
			if clk_fre != "": #有时钟频率
				clk_fre, clk_cycle = clk_fre_to_cycle(clk_fre, hz_mode)
			else: #有时钟周期
				clk_fre, clk_cycle = clk_fre_to_cycle(clk_fre, hz_mode)
			sig_wd  = sig_wd_to_bit(sig_wd, wd_mode)
			bd_perf = clk_width_to_bound(clk_cycle, sig_wd)
		else: #由频率和带宽推可以支撑位宽
			if clk_fre != "": #有时钟频率
				clk_fre, clk_cycle = clk_fre_to_cycle(clk_fre, hz_mode)
			else: #有时钟周期
				clk_fre, clk_cycle = clk_cycle_to_fre(clk_cycle, ns_mode)
				print("有时钟周期")
			bd_perf = bd_perf_to_bps(bd_perf, bd_mode)
			sig_wd  = bound_clk_to_width(bd_perf, clk_cycle)
		
		print(clk_fre, clk_cycle, sig_wd, bd_perf)

		p_clk_fre   = gen_print_clk_fre(clk_fre, hz_mode)
		p_clk_cycle = gen_print_clk_cycle(clk_cycle, ns_mode)
		p_sig_wd    = gen_print_sig_wd(sig_wd, wd_mode)
		p_bd_perf   = gen_print_bd_perf(bd_perf, bd_mode)

		txt_hz.delete(0.0, END)
		txt_hz.insert(1.0, p_clk_fre)
		txt_ns.delete(0.0, END)
		txt_ns.insert(1.0, p_clk_cycle)
		txt_sig.delete(0.0, END)
		txt_sig.insert(1.0, p_sig_wd)
		txt_bd.delete(0.0, END)
		txt_bd.insert(1.0, p_bd_perf)
	pass

	def flush_txt():
		txt_hz.delete(0.0, END)
		txt_ns.delete(0.0, END)
		txt_sig.delete(0.0, END)
		txt_bd.delete(0.0, END)
	pass

	root = Tk()
	root.geometry("210x260")
	root.title("带宽估算器 from 尼德兰的喵")

	#选择基准
	sel_input_v = IntVar()
	sel_input_v.set(2)
	f_sel_input = LabelFrame(root, text = "选择参考对象", height = 100, width = 300)
	f_sel_input.pack(fill=X, pady=1)
	bandwidth = Radiobutton(f_sel_input, text="以带宽为基准", variable=sel_input_v, value=1)
	signalwidth = Radiobutton(f_sel_input, text="以时钟为基准", variable=sel_input_v, value=2)
	bandwidth.pack()
	signalwidth.pack()

	#时钟赋值区域
	f_clk = Frame(root, height = 100, width = 300)
	f_clk.pack(fill=X, pady=5)
	#频率
	labe_hz = Label(f_clk,text='时钟频率').pack(side=LEFT)
	txt_hz  = Text(f_clk, height=1, width = txt_wd)
	txt_hz.pack(side = LEFT)
	#时钟单位的下拉菜单
	hz_com = ttk.Combobox(f_clk, textvariable=StringVar(), cursor="arrow", width=com_wd)
	hz_com["value"] = ("KHz", "MHz", "GHz")
	hz_com.current(1)
	hz_com.bind("<<ComboboxSelected>>", hz_mode_choose)
	hz_com.pack(side = LEFT)	
	
	#周期
	f_time = Frame(root, height = 100, width = 300)
	f_time.pack(fill=X, pady=5)
	labe_ns = Label(f_time,text='时钟周期').pack(side=LEFT)
	txt_ns  = Text(f_time, height=1, width = txt_wd)
	txt_ns.pack(side = LEFT)
	#周期单位的下拉菜单
	ns_com = ttk.Combobox(f_time, textvariable=StringVar(), cursor="arrow", width=com_wd)
	ns_com["value"] = ("ms", "μs", "ns", "ps")
	ns_com.current(2)
	ns_com.bind("<<ComboboxSelected>>", ns_mode_choose)
	ns_com.pack(side = LEFT)
	
	#信号位宽
	f_sig = Frame(root, height = 100, width = 300)
	f_sig.pack(fill=X, pady=5)
	labe_sig = Label(f_sig,text='信号位宽').pack(side=LEFT)
	txt_sig  = Text(f_sig, height=1, width = txt_wd)
	txt_sig.pack(side = LEFT)
	#位宽的下拉菜单
	sig_com = ttk.Combobox(f_sig, textvariable=StringVar(), cursor="arrow", width=com_wd)
	sig_com["value"] = ("bit", "Byte")
	sig_com.current(0)
	sig_com.bind("<<ComboboxSelected>>", wd_mode_choose)
	sig_com.pack(side = LEFT)

	#通路带宽
	f_bd = Frame(root, height = 100, width = 300)
	f_bd.pack(fill=X, pady=5)
	labe_bd = Label(f_bd,text='通路带宽').pack(side=LEFT)
	txt_bd  = Text(f_bd, height=1, width = txt_wd)
	txt_bd.pack(side = LEFT)
	#通路带宽的下拉菜单	
	bd_com = ttk.Combobox(f_bd, textvariable=StringVar(), cursor="arrow", width=com_wd)
	bd_com["value"] = ("Kbps", "Mbps", "Gbps", "Tbps")
	bd_com.current(2)
	bd_com.bind("<<ComboboxSelected>>", bd_mode_choose)
	bd_com.pack(side = LEFT)

	fx = Frame(root, height = 100, width = 300)
	fx.pack(pady=10)
	button1 = Button(fx, text='确定', command=start_trans)
	button1.pack(side=LEFT, padx=10)
	button2 = Button(fx, text='退出', command=root.quit)
	button2.pack(side=RIGHT, padx=10)
	button1 = Button(fx, text='刷新', command=flush_txt)
	button1.pack(side=RIGHT, padx=10)

	root.mainloop()

if __name__ == '__main__':
	tk_main()